package com.hins.component.hlock.core;

import com.hins.component.hlock.annotation.Hlock;
import com.hins.component.hlock.config.HlockConfig;
import lombok.Setter;
import com.hins.component.hlock.model.LockInfo;
import com.hins.component.hlock.model.LockType;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

/**
 * @author Leslie
 * @date 2021/9/7 17:30
 */
public class LockInfoProvider {

    private static final Logger logger = LoggerFactory.getLogger(LockInfoProvider.class);

    private static final String LOCK_NAME_PREFIX = "lock";
    private static final String LOCK_NAME_SEPARATOR = ".";

    @Setter(onMethod = @__(@Autowired))
    private HlockConfig hlockConfig;

    @Setter(onMethod = @__(@Autowired))
    private BusinessKeyProvider businessKeyProvider;

    public LockInfo get(JoinPoint joinPoint, Hlock hlock) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();

        LockType type = hlock.lockType();

        String businessKeyName = businessKeyProvider.getKeyName(joinPoint, hlock);

        // 锁的名字，锁的粒度就是这里控制的
        String lockName = LOCK_NAME_PREFIX + LOCK_NAME_SEPARATOR + getName(hlock.name(), signature) + businessKeyName;

        long waitTime = getWaitTime(hlock);
        long leaseTime = getLeaseTime(hlock);

        //如果占用锁的时间设计不合理，则打印相应的警告提示
        if (leaseTime == -1 && logger.isWarnEnabled()) {
            logger.warn("Trying to acquire Lock({}) with no expiration, " +
                    "Hlock will keep prolong the com.hins.component.hlock.lock expiration while the com.hins.component.hlock.lock is still holding by current thread. " +
                    "This may cause dead com.hins.component.hlock.lock in some circumstances.", lockName);
        }

        return new LockInfo(type, lockName, waitTime, leaseTime);
    }

    /**
     * 获取锁的name，如果没有指定，则按全类名拼接方法名处理
     *
     * @param annotationName 指定hlock注解的name
     * @param signature      MethodSignature
     * @return name
     */
    private String getName(String annotationName, MethodSignature signature) {
        if (annotationName.isEmpty()) {
            return String.format("%s.%s", signature.getDeclaringTypeName(), signature.getMethod().getName());
        } else {
            return annotationName;
        }
    }

    private long getWaitTime(Hlock lock) {
        return lock.waitTime() == Long.MIN_VALUE ?
                hlockConfig.getWaitTime() : lock.waitTime();
    }

    private long getLeaseTime(Hlock lock) {
        return lock.leaseTime() == Long.MIN_VALUE ?
                hlockConfig.getLeaseTime() : lock.leaseTime();
    }
}
